﻿// 
// $Id: ScriptManager.cs 3981 2010-11-02 11:12:16Z unknown $
// 
// Zen UI Toolkit Copyright (c) 2010, Paul Clancy.  All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without modification, are permitted provided 
// that the following conditions are met:
// 
// * 	Redistributions of source code must retain the above copyright notice, this list of 
// 	conditions and the following disclaimer.
// 
// * 	Redistributions in binary form must reproduce the above copyright notice, this list of 
// 	conditions and the following disclaimer in the documentation and/or other materials 
// 	provided with the distribution.
// 
// * 	Neither the name of AppTek nor the names of its contributors may be used to endorse 
// 	or promote products derived from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
// THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 

#region

using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.UI;

#endregion

namespace Zen.Toolkit
{
    public enum Placement
    {
        InHead,
        InBody,
        OnLoad,
        OnBeforeInit,
        OnInit,
        OnAfterInit,
        OnLazyInit
    }

    public class ScriptFragment
    {
        public ScriptFragment()
        {
            Template = ScriptTemplate.NullFragment;
            Script = new object[] { "" };
        }

        public IScriptTemplate Template { get; set; }
        public Placement Position { get; set; }
        public object[] Script { get; set; }
        public int Order { get; set; }

        public string Build()
        {
            return Template.Build(Script);
        }
    }

    [ToolboxBitmap(typeof(ScriptManager), "Resources.Icons.Toolbox.script.bmp")]
    public class ScriptManager : WidgetBase
    {
        private readonly Dictionary<string, ScriptFragment> _fragments = new Dictionary<string, ScriptFragment>();
        private bool _scriptRendered;

        public ScriptManager()
            : base(HtmlTextWriterTag.Div)
        {
        }

        //private bool IsFragmentRegistered(string key) { return false; }


        public void AddFragment(int ordinal, string key, Placement position, string scriptContent)
        {
            _fragments[key] = new ScriptFragment
                                  {
                                      Order = ordinal,
                                      Position = position,
                                      Template = ScriptTemplate.NullFragment,
                                      Script = new object[] { scriptContent }
                                  };
        }

        public void AddFragment(int ordinal, string key, Placement placement, IScriptTemplate template, params object[] args)
        {
            _fragments[key] = new ScriptFragment
                                  {
                                      Order = ordinal,
                                      Position = placement,
                                      Template = template,
                                      Script = args
                                  };
        }

        public void AddIncludeReference(string url)
        {
            AddFragment(0, string.Format("@incref:{0}", url),
                        Placement.InHead,
                        ScriptTemplate.IncludeScript,
                        url);
        }

        public void AddIncludeResource(Page page, string resourceName)
        {
            string resourceUrl = page.ClientScript.GetWebResourceUrl(GetType(), resourceName);
            AddFragment(0, string.Format("@incres:{0}", resourceName),
                        Placement.InHead, ScriptTemplate.IncludeScript, resourceUrl);
        }

        public void AddStyleResource(Page page, string resourceName)
        {
            string resourceUrl = page.ClientScript.GetWebResourceUrl(GetType(), resourceName);

            AddFragment(0,
                string.Format("@cssres:{0}", resourceUrl),
                Placement.InHead, ScriptTemplate.ImportStyle, resourceUrl
                );
        }

        public void AddRequireScript(string moduleName)
        {
            AddFragment(0,
                string.Format("@reqmod:{0}", moduleName),
                Placement.InHead, ScriptTemplate.DojoRequire, moduleName);
        }

        public void AddAfterWidgetInit(WidgetBase widget, string key, string format, params object[] args)
        {
            AddFragment(widget.RenderOrder, widget.ClientID + key, Placement.OnAfterInit, string.Format(format, args));
        }

        public void AddLazyWidgetInit(WidgetBase widget, string key, string format, params object[] args)
        {
            AddFragment(widget.RenderOrder, widget.ClientID + key, Placement.OnLazyInit, string.Format(format, args));
        }

        public void AddWidgetInitWithNode(WidgetBase widget, string widgetName, string constructorArgs, string nodeName)
        {
            AddFragment(
                widget.RenderOrder,
                widget.ClientID + "_init",
                Placement.OnInit,
                ScriptTemplate.DojoConstructorWithNode,
                new object[]
                    {
                        widget.DojoID,
                        widgetName,
                        constructorArgs,
                        nodeName
                    });
        }

        public void AddWidgetInit(WidgetBase widget, string widgetName, string constructorArgs)
        {
            AddFragment(
                widget.RenderOrder,
                widget.ClientID + "_init",
                Placement.OnInit,
                ScriptTemplate.DojoConstructor,
                new object[]
                    {
                        widget.DojoID,
                        widgetName,
                        constructorArgs
                    });
        }

        public void AddOnLoadScript(string key, string script)
        {
            AddFragment(0, key, Placement.OnBeforeInit, script);
        }

        public void AddStyleReference(string url)
        {
            AddFragment(
                0,
                string.Format("@cssref:{0}", url),
                Placement.InHead, ScriptTemplate.ImportStyle, url
                );
        }


        internal void WriteScript(StringWriter writer, ScriptFragment[] fragments)
        {
            foreach (ScriptFragment f in fragments)
            {
                writer.WriteLine(f.Build());
            }
        }

        public static string Format(string format, params object[] args)
        {
            return string.Format(new ZenFormatter(), format, args);
        }

        public string BuildScript()
        {
            var scriptBuffer = new StringBuilder();
            var scriptBufferWriter = new StringWriter(scriptBuffer);

            if (!Z.IsZenCallback)
            {
                WriteScript(scriptBufferWriter,
                            (from n in _fragments.Values
                             where
                                 n.Position == Placement.InHead &&
                                 ((n.Template.Flags & ScriptTemplateFlags.Block) == ScriptTemplateFlags.Block)
                             select n).OrderBy(x => x.Order).ToArray()
                    );

                // write all inline header script
                scriptBufferWriter.WriteLine("<script type=\"text/javascript\">");
                WriteScript(scriptBufferWriter,
                            (from n in _fragments.Values
                             where
                                 n.Position == Placement.InHead &&
                                 ((n.Template.Flags & ScriptTemplateFlags.Inline) == ScriptTemplateFlags.Inline)
                             select n).OrderBy(x => x.Order).ToArray()
                    );
            }


            // write all dojo onload script

            if (!Z.IsZenCallback)
            {
                // begin dojo onload
                scriptBufferWriter.WriteLine("dojo.addOnLoad(function() {");
            }

            WriteScript(scriptBufferWriter,
                        (from n in _fragments.Values
                         where n.Position == Placement.OnLoad
                         select n).OrderBy(x => x.Order).ToArray()
                );

            WriteScript(scriptBufferWriter,
                        (from n in _fragments.Values
                         where n.Position == Placement.OnBeforeInit
                         orderby (RenderOrder)
                         select n).OrderBy(x => x.Order).ToArray()
                );

            WriteScript(scriptBufferWriter,
                        (from n in _fragments.Values
                         where n.Position == Placement.OnInit
                         orderby (RenderOrder)
                         select n).OrderBy(x => x.Order).ToArray()
                );

            WriteScript(scriptBufferWriter,
                        (from n in _fragments.Values
                         where n.Position == Placement.OnAfterInit
                         orderby (RenderOrder)
                         select n).OrderBy(x => x.Order).ToArray()
                );

            WriteScript(scriptBufferWriter,
                        (from n in _fragments.Values
                         where n.Position == Placement.OnLazyInit
                         orderby (RenderOrder)
                         select n).OrderBy(x => x.Order).ToArray()
                );

            if (!Z.IsZenCallback)
            {
                // end dojo onload
                scriptBufferWriter.WriteLine("});");
                scriptBufferWriter.WriteLine("</script>");
            }

            return scriptBufferWriter.ToString();
        }

        protected override void Render(HtmlTextWriter writer)
        {
            if (!DesignMode)
            {
                if (_scriptRendered)
                    throw (new ZenScriptException("Script has already been rendered"));

                writer.Write(BuildScript());
                _scriptRendered = true;
            }
        }
    }
}